/**
 * 
 */
package com.aspire.android.heliar.utils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.text.NumberFormat;
import java.util.List;

import android.os.Environment;
import android.os.StatFs;

/**
 * @author panglei
 * 
 */
public class FileSystem {

	private static final int BUFFER_SIZE = 1024;

	/**
	 * ��ȡָ���ļ�Ŀ¼�п��õĴ��̿ռ��С����λByte
	 * 
	 * @param path
	 *            ָ��Ŀ¼���
	 * @return ���ؿ��ô��̿ռ��С
	 */
	public static long getAvailableSpace(String path) {
		StatFs statFs = new StatFs(path);
		int blockSize = statFs.getBlockSize();
		return statFs.getAvailableBlocks() * blockSize;
	}

	/**
	 * ��ȡָ��Ŀ¼���̿ռ��С����λByte
	 * 
	 * @param path
	 *            ָ��Ŀ¼���
	 * @return ����ָ��Ŀ¼���̿ռ��С
	 */
	public static long getTotalSpace(String path) {
		StatFs statFs = new StatFs(path);
		int blockSize = statFs.getBlockSize();
		return statFs.getBlockCount() * blockSize;
	}

	/**
	 * �ж��ⲿ�洢���Ƿ����
	 * 
	 * @return ���÷���true�������÷���false
	 */
	public static boolean isExternalStorageAvailable() {
		boolean externalStorageAvailable = false;
		String state = Environment.getExternalStorageState();

		if (Environment.MEDIA_MOUNTED.equals(state)) {
			externalStorageAvailable = true;
		} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
			externalStorageAvailable = true;
		} else {
			externalStorageAvailable = false;
		}
		return externalStorageAvailable;

	}

	/**
	 * �ж��ⲿ�洢��״̬�Ƿ����д��
	 * 
	 * @return ���÷���true, �����÷���false
	 */
	public static boolean isExternalStorageWriteable() {
		boolean externalStorageWriteable = false;
		String state = Environment.getExternalStorageState();

		if (Environment.MEDIA_MOUNTED.equals(state)) {
			externalStorageWriteable = true;
		} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
			externalStorageWriteable = false;
		} else {
			externalStorageWriteable = false;
		}

		return externalStorageWriteable;
	}

	/**
	 * �����ļ���Ŀ¼��ָ�����ļ���Ŀ¼��
	 * 
	 * @param source
	 *            Դ�ļ���Ŀ¼
	 * @param target
	 *            Ŀ���ļ���Ŀ¼
	 * @param override
	 *            �Ƿ񸲸�
	 * 
	 * @return ���������Ľ��
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 */
	public static boolean copy(File source, File target, boolean override)
			throws IOException {

		boolean result = false;

		if (source == null) {
			throw new IOException("Parameter[source] is null!");
		} else if (target == null) {
			throw new IOException("Parameter[target] is null!");
		} else if (!source.exists()) {
			throw new IOException("Parameter[source=" + source
					+ "] did not exist!");
		} else {
			if (source.isFile()) {
				if (!target.exists()) {
					if ((target.getParentFile() != null)
							&& (target.getParentFile().exists())) {
						writeFile(source, target);
						result = true;
					} else {
						throw new IOException("Parent directory["
								+ target.getParentFile()
								+ "] of the parameter[target=" + target
								+ "] did not exist!");
					}
				} else {
					if (target.isFile()) {
						if (override && delete(target)) {
							writeFile(source, target);
							result = true;
						}
					} else if (target.isDirectory()) {
						File sameNameFile = getSameNameFile(source,
								target.listFiles());
						if (sameNameFile != null) {
							if (sameNameFile.isFile()) {
								if (override && delete(sameNameFile)) {
									writeFile(source, sameNameFile);
									result = true;
								}
							} else if (sameNameFile.isDirectory()) {
								throw new IOException(
										"Parameter[target="
												+ target
												+ "] included a same name directory as parameter[source="
												+ source + "]!");
							}
						} else {
							writeFile(source,
									new File(target, source.getName()));
							result = true;
						}
					}
				}
			} else if (source.isDirectory()) {
				if (!target.exists()) {
					throw new IOException("Parameter[target=" + target
							+ "] did not exist!");
				} else {
					if (target.isFile()) {
						throw new IOException(
								"Parameter[target="
										+ target
										+ "] is a file, Directory can't copy to a file!");
					} else if (target.isDirectory()) {
						File sameNameFile = getSameNameFile(source,
								target.listFiles());
						if (sameNameFile != null) {
							if (sameNameFile.isFile()) {
								throw new IOException(
										"Parameter[target="
												+ target
												+ "] included a same name file as parameter[source="
												+ source + "]!");
							} else if (sameNameFile.isDirectory()) {
								File[] files = source.listFiles();
								for (int i = 0; i < files.length; i++) {
									if (!copy(files[i], sameNameFile, override)) {
										return false;
									}
								}
								result = true;
							}
						} else {
							File sourceInTarget = new File(target,
									source.getName());
							if (sourceInTarget.mkdir()) {
								File[] files = source.listFiles();
								for (int i = 0; i < files.length; i++) {
									if (!copy(files[i], sourceInTarget,
											override)) {
										return false;
									}
								}
								result = true;
							}
						}
					}
				}
			}
		}

		return result;
	}

	/**
	 * ����һ�������ļ���Ŀ¼��ָ�����ļ���Ŀ¼��
	 * 
	 * @param source
	 *            Դ�ļ���Ŀ¼��һ��������
	 * @param target
	 *            Ŀ���ļ���Ŀ¼
	 * @param override
	 *            �Ƿ񸲸�
	 * 
	 * @return ���������Ľ��
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 */
	public static boolean copy(List<File> source, File target, boolean override)
			throws IOException {

		boolean result = true;

		if (source == null) {
			throw new IOException("Parameter[source] is null!");
		} else if (target == null) {
			throw new IOException("Parameter[target] is null!");
		} else {
			for (int i = 0; i < source.size(); i++) {
				result = result && copy(source.get(i), target, override);
				if (!result) {
					break;
				}
			}
		}

		return result;
	}

	/**
	 * �ƶ��ļ���Ŀ¼��ָ�����ļ���Ŀ¼��
	 * 
	 * @param source
	 *            Դ�ļ���Ŀ¼
	 * @param target
	 *            Ŀ���ļ���Ŀ¼
	 * @param override
	 *            �Ƿ񸲸�
	 * 
	 * @return �ƶ������Ľ��
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 */
	public static boolean move(File source, File target, boolean override)
			throws IOException {

		return copy(source, target, override) && delete(source);
	}

	/**
	 * �ƶ�һ�������ļ���Ŀ¼��ָ�����ļ���Ŀ¼��
	 * 
	 * @param source
	 *            Դ�ļ���Ŀ¼��һ��������
	 * @param target
	 *            Ŀ���ļ���Ŀ¼
	 * @param override
	 *            �Ƿ񸲸�
	 * 
	 * @return �ƶ������Ľ��
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 */
	public static boolean move(List<File> source, File target, boolean override)
			throws IOException {

		boolean result = true;

		if (source == null) {
			throw new IOException("Parameter[source] is null!");
		} else if (target == null) {
			throw new IOException("Parameter[target] is null!");
		} else {
			for (int i = 0; i < source.size(); i++) {
				result = result && move(source.get(i), target, override);
				if (!result) {
					break;
				}
			}
		}

		return result;
	}

	/**
	 * ɾ��Ŀ¼�����ļ���
	 * 
	 * @param target
	 *            Ҫɾ���Ŀ¼�����ļ���
	 * 
	 * @return ɾ������Ľ��
	 */
	public static boolean delete(File target) {

		boolean result = false;

		if ((target != null) && (target.exists())) {
			if (target.isFile()) {
				result = target.delete();
			} else if (target.isDirectory()) {
				File[] files = target.listFiles();
				for (int i = 0; i < files.length; i++) {
					delete(files[i]);
				}
				result = target.delete();
			}
		}

		return result;
	}

	private static void writeFile(File from, File to) throws IOException {

		if (from == null) {
			throw new IOException("Parameter[from] is null!");
		} else if (to == null) {
			throw new IOException("Parameter[to] is null!");
		} else if (!from.exists()) {
			throw new IOException("Parameter[from=" + from + "] did not exist!");
		} else if (!from.isFile()) {
			throw new IOException("Parameter[from=" + from + "] is not a file!");
		} else {
			BufferedInputStream in = new BufferedInputStream(
					new FileInputStream(from));
			BufferedOutputStream out = new BufferedOutputStream(
					new FileOutputStream(to));
			try {
				byte[] buf = new byte[BUFFER_SIZE];
				int count = -1;
				while ((count = in.read(buf, 0, BUFFER_SIZE)) != -1) {
					out.write(buf, 0, count);
				}
			} finally {
				try {
					out.close();
				} catch (Exception e) {
				}
				try {
					in.close();
				} catch (Exception e) {
				}
			}
		}
	}

	private static File getSameNameFile(File target, File[] scope) {

		File result = null;

		if ((target != null) && (scope != null)) {
			for (int i = 0; i < scope.length; i++) {
				if (System.getProperty("os.name").toLowerCase()
						.indexOf("windows") != -1) {
					if (scope[i].getName().equalsIgnoreCase(target.getName())) {
						result = scope[i];
						break;
					}
				} else {
					if (scope[i].getName().equals(target.getName())) {
						result = scope[i];
						break;
					}
				}
			}
		}

		return result;
	}

	/**
	 * ����ļ���չ��
	 * 
	 * @param filepath
	 *            �ļ�·��
	 * 
	 * @return �ļ���չ�����û����չ�������Ŀ¼���򷵻�null��
	 */
	public static String getFilenameExtension(String filepath) {

		if (filepath == null) {
			return null;
		} else if ("".equals(filepath)) {
			return "";
		} else {
			int dotPos = filepath.lastIndexOf(".");
			if (dotPos != -1) {
				return filepath.substring(dotPos + 1);
			} else {
				return "";
			}
		}
	}

	/**
	 * ����ļ���չ��
	 * 
	 * @param file
	 *            �ļ�����
	 * 
	 * @return �ļ���չ������ļ������ڻ���û����չ�������Ŀ¼���򷵻�null��
	 */
	public static String getFilenameExtension(File file) {

		if (file == null) {
			return null;
		} else {
			return getFilenameExtension(file.getName());
		}
	}

	/**
	 * ɾ��Ŀ¼����Ŀ¼
	 * 
	 * @param dir
	 *            Ŀ¼���
	 * 
	 * @return ɾ��ɹ�����true�����򷵻�false
	 */
	public static boolean deleteDir(File dir) {

		if (dir.isDirectory()) {
			String[] children = dir.list();
			for (int i = 0; i < children.length; i++) {
				boolean success = deleteDir(new File(dir, children[i]));
				if (!success) {
					return false;
				}
			}
		}

		return dir.delete();
	}

	/**
	 * ��ȡ�ļ����ݡ�
	 * 
	 * @param file
	 *            �ļ�����
	 * @param charset
	 *            �ļ��ַ�
	 * 
	 * @return �ļ�����
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 */
	public static String readFile(File file, String charset) throws IOException {

		StringBuilder result = new StringBuilder();

		Reader reader = null;
		try {
			reader = new BufferedReader(new InputStreamReader(
					new FileInputStream(file), charset));
			char[] cbuf = new char[BUFFER_SIZE];
			int len = -1;
			while ((len = reader.read(cbuf, 0, cbuf.length)) != -1) {
				result.append(cbuf, 0, len);
			}
		} finally {
			try {
				reader.close();
			} catch (Exception e) {
			}
		}

		return result.toString();
	}

	/**
	 * д���ļ���
	 * 
	 * @param content
	 *            �ļ�����
	 * @param file
	 *            �ļ�����
	 * @param charset
	 *            �ļ��ַ�
	 * @param append
	 *            ���Ϊ true�����ֽ�д���ļ�ĩβ��������д���ļ���ʼ��
	 * 
	 * @throws IOException
	 *             ������I/O����ʱ�׳���
	 */
	public static void writeFile(String content, File file, String charset,
			boolean append) throws IOException {

		Writer writer = null;
		try {
			writer = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(file, append), charset));
			writer.write(content);
			writer.flush();
		} finally {
			try {
				writer.close();
			} catch (Exception e) {
			}
		}
	}

	/**
	 * ��ʽ���ļ���С
	 * 
	 * @param fileSize
	 *            �ļ���С��file.size()
	 * @param decimalPos
	 *            ����С����λ��
	 * 
	 * @return ��ʽ�����ļ���С�ַ�
	 */
	public static String formatFileSize(long fileSize, int decimalPos) {

		NumberFormat fmt = NumberFormat.getNumberInstance();
		if (decimalPos >= 0) {
			fmt.setMaximumFractionDigits(decimalPos);
		}

		double val = fileSize / (1024 * 1024);
		if (val > 1) {
			return fmt.format(val).concat(" MB");
		}
		val = fileSize / 1024;
		if (val > 10) {
			return fmt.format(val).concat(" KB");
		}

		return fmt.format(val).concat(" Bytes");
	}
}
